////////////////////////////////////////////////////////////////
// Object3D_GL.cpp
// OpenGL 3DObject implmentation
//
////////////////////////////////////////////////////////////////

#include "Object3D_GL.h"
#include "..\Toolbox\MyTrace.h"

ObjectBone::~ObjectBone(void){
	while (!_Bones.empty())
   	{delete _Bones.back(); _Bones.pop_back();}
}

void ObjectBone::CalcAttitude(ObjectBone* &pParentBone){
  	if (pParentBone == 0)
	   _CombinedMatrix = _TransformMatrix;
   else
      _CombinedMatrix = _TransformMatrix * pParentBone->_CombinedMatrix;

   _FinalMatrix = _Bone->_SkinOffset * _CombinedMatrix;
}/**/

/*************************************************
NEW- NEW- NEW- NEW- NEW- NEW- NEW- NEW- NEW- NEW*/

void ObjectBone::CalcAnimation(uint16 &pKey){
	if (_Animation == 0)
   	return;

   Frm::Matrix Mat;
   float DeltaKey; //difference between two time keys
   float cTime; //pKey minus current key time: cTime / DeltaKey gives the interpolation factor

   //If the transformations are expressed as matrices
	if (!_Animation->_Matrices.empty())
     	{
      if (pKey == 0)
      	_AnimationIndexMat = 0;

      if (_AnimationIndexMat == _Animation->_Matrices.size()-1)
      	//If we are at the last matrix index use the last matrix as a transform matrix
  	     	Mat = (_Animation->_Matrices[_AnimationIndexMat])->Matrix;
  	 	else
     		{
         //Advance the internal matrix animation index according to the main Time count
     		while (pKey > (_Animation->_Matrices[_AnimationIndexMat+1])->Time)
        		{
      		_AnimationIndexMat++;
         	if (_AnimationIndexMat >= _Animation->_Matrices.size())
           		{
     				_AnimationIndexMat = _Animation->_Matrices.size() - 1;
              	break;
              	}
           	}

         //interpolate the transform matrix between this matrix index and
         //the next using the Time count parameter
       	DeltaKey = (_Animation->_Matrices[_AnimationIndexMat + 1])->Time - (_Animation->_Matrices[_AnimationIndexMat])->Time;
			cTime = pKey - (_Animation->_Matrices[_AnimationIndexMat])->Time;
        	Mat = (_Animation->_Matrices[_AnimationIndexMat])->Matrix + (((_Animation->_Matrices[_AnimationIndexMat+1])->Matrix - (_Animation->_Matrices[_AnimationIndexMat])->Matrix) * (cTime / DeltaKey));
        	}
      _TransformMatrix = Mat;
      }
   else
   	{
      Frm::Quaternion Quat;
      Frm::Vector Scale;
      Frm::Vertex Translate;

     	_TransformMatrix.Identity();
      if (pKey == 0)
      	{
      	_AnimationIndexR = 0;
         _AnimationIndexS = 0;
         _AnimationIndexT = 0;
         }

      //If there are rotations
     	if (!_Animation->_Rotations.empty())
     		{
        	if (_AnimationIndexR == _Animation->_Rotations.size()-1)
         	//If we are at the last rotation index use the last rotation
        		Quat = (_Animation->_Rotations[_AnimationIndexR])->Rotation;
        	else
        		{
            //Advance the internal rotation animation index according to the main Time count
	     		while (pKey > (_Animation->_Rotations[_AnimationIndexR+1])->Time)
   	     		{
      			_AnimationIndexR++;
         		if (_AnimationIndexR >= _Animation->_Rotations.size())
           			{
     					_AnimationIndexR = _Animation->_Rotations.size() - 1;
              		break;
              		}
           		}
            //Interpolate the quaternion
           	DeltaKey = (_Animation->_Rotations[_AnimationIndexR + 1])->Time - (_Animation->_Rotations[_AnimationIndexR])->Time;
				cTime = pKey - (_Animation->_Rotations[_AnimationIndexR])->Time;
           	Quat = (_Animation->_Rotations[_AnimationIndexR])->Rotation.Slerp(cTime / DeltaKey, (_Animation->_Rotations[_AnimationIndexR+1])->Rotation);
           	}
        	Mat.Identity();
        	Mat.QuaternionMatrix(Quat[1], Quat[2], Quat[3], Quat[0]);
  			_TransformMatrix *= Mat;
     		}

      //if there are scalings
      if (!_Animation->_Scalings.empty())
     		{
         if (_AnimationIndexS == _Animation->_Scalings.size()-1)
         	//If we are at the last scaling index use the last scaling
         	Scale = (_Animation->_Scalings[_AnimationIndexS])->Scale;
         else
         	{
            //Advance the internal scaling animation index according to the main Time count
	     		while (pKey > (_Animation->_Scalings[_AnimationIndexS+1])->Time)
   	     		{
      			_AnimationIndexS++;
         		if (_AnimationIndexS >= _Animation->_Scalings.size())
           			{
     					_AnimationIndexS = _Animation->_Scalings.size() - 1;
              		break;
              		}
           		}
            //Interpolate the vector
            DeltaKey = (_Animation->_Scalings[_AnimationIndexS + 1])->Time - (_Animation->_Scalings[_AnimationIndexS])->Time;
				cTime = pKey - (_Animation->_Scalings[_AnimationIndexS])->Time;
            Scale = (_Animation->_Scalings[_AnimationIndexS])->Scale + (((_Animation->_Scalings[_AnimationIndexS+1])->Scale - (_Animation->_Scalings[_AnimationIndexS])->Scale) * (cTime / DeltaKey));
            }

         Mat.Identity();
         Mat.ScalingMatrix(Scale);
     		_TransformMatrix *= Mat;
      	}

      //if there are translations
      if (!_Animation->_Translations.empty())
     		{
         if (_AnimationIndexT == _Animation->_Translations.size()-1)
         	//If we are at the last translation index use the last translation
         	Translate = (_Animation->_Translations[_AnimationIndexT])->Translation;
         else
         	{
            //Advance the internal translation animation index according to the main Time count
	     		while (pKey > (_Animation->_Translations[_AnimationIndexT+1])->Time)
   	     		{
      			_AnimationIndexT++;
         		if (_AnimationIndexT >= _Animation->_Translations.size())
           			{
     					_AnimationIndexT = _Animation->_Translations.size() - 1;
              		break;
              		}
           		}
            //interpolate the vector
            DeltaKey = (_Animation->_Translations[_AnimationIndexT + 1])->Time - (_Animation->_Translations[_AnimationIndexT])->Time;
				cTime = pKey - (_Animation->_Translations[_AnimationIndexT])->Time;
            Translate = (_Animation->_Translations[_AnimationIndexT])->Translation + (((_Animation->_Translations[_AnimationIndexT+1])->Translation - (_Animation->_Translations[_AnimationIndexT])->Translation) * (cTime / DeltaKey));
            }
         Mat.Identity();
         Mat.TranslationMatrix(Translate);
     		_TransformMatrix *= Mat;
      	}
      }
}/**/


void Object3D::MapAnimationSet(std::string pText)
{
    _cAnimationSet = _Model->FindAnimationSet(pText);
    _cKey = 0;
    if (_cAnimationSet!=0)
    	GetBoneAnimation(_Skeletton);
}

void Object3D::MapAnimationSet(uint16 &index)
{
  	if (index >= _Model->_AnimationSets.size())
     	index = 0;
   MYTRACE(_Model->_AnimationSets.size(), "Animation Sets. Playing:", index);
   std::list<Frm::AnimationSet*>::iterator i = _Model->_AnimationSets.begin();
   if (index != 0)
   	{
   	int16 test = index;
   	while (test--)
     		i++;
      }
  	_cAnimationSet = (*i);

   GetBoneAnimation(_Skeletton);
   _cKey = 0;
}
/***END*******************************************/

void Object3D::Draw(Screen_GL* pScreen)
{
	Frm::Subset* TempSubset;
   Frm::Face TempFace;
   std::list<Frm::Subset*>::iterator i = _Mesh->_Subsets.begin();
   std::list<Frm::Material*>::iterator j = _Mesh->_Materials.begin();

   while (j != _Mesh->_Materials.end())
   	{
      pScreen->SetMaterial(*j);
      TempSubset = *i;
	   glBegin(GL_TRIANGLES);
   	  	for(int k = 0; k < TempSubset->Size; k++)
      	  	{
            TempFace = TempSubset->Faces[k];
         	glTexCoord2fv(_Mesh->_TextureCoords[TempFace.data[0]].data);
         	glVertex3fv(_SkinnedVertices[TempFace.data[0]].data);
         	glTexCoord2fv(_Mesh->_TextureCoords[TempFace.data[1]].data);
         	glVertex3fv(_SkinnedVertices[TempFace.data[1]].data);
         	glTexCoord2fv(_Mesh->_TextureCoords[TempFace.data[2]].data);
         	glVertex3fv(_SkinnedVertices[TempFace.data[2]].data);
         	}
   	glEnd();
      i++;
      j++;
      }
}

ObjectBone* Object3D::ReplicateSkeletton(Frm::Bone* &pBone)
{
	ObjectBone* NBone = new ObjectBone;

   NBone->_BoneName = pBone->_Name;
   NBone->_Bone = pBone;
   NBone->_TransformMatrix = pBone->_MatrixPos;

   if (!pBone->_Bones.empty())
      for (std::list<Frm::Bone*>::iterator i = pBone->_Bones.begin(); i != pBone->_Bones.end(); i++)
			NBone->_Bones.push_back(ReplicateSkeletton(*i));
   return NBone;
}

/*************************************************
NEW- NEW- NEW- NEW- NEW- NEW- NEW- NEW- NEW- NEW*/
void Object3D::GetBoneAnimation(ObjectBone* &pBone)
{
   pBone->_Animation = _cAnimationSet->FindAnimation(pBone->_BoneName);
   if (pBone->_Animation == 0)
   	MYTRACE(pBone->_BoneName, "is not linked to an animation.");
   pBone->_AnimationIndexMat = 0;
   pBone->_AnimationIndexR = 0;
   pBone->_AnimationIndexS = 0;
   pBone->_AnimationIndexT = 0;
	if (!pBone->_Bones.empty())
 		for(std::list<ObjectBone*>::iterator i = pBone->_Bones.begin(); i != pBone->_Bones.end(); i++)
        	GetBoneAnimation(*i);
}
/***END*******************************************/

void Object3D::CalcAttitude(ObjectBone* pBone, ObjectBone* pParentBone)
{
	pBone->CalcAttitude(pParentBone);
   if (!pBone->_Bones.empty())
 		for(std::list<ObjectBone*>::iterator i = pBone->_Bones.begin(); i != pBone->_Bones.end(); i++)
        	CalcAttitude(*i, pBone);
}

/*************************************************
NEW- NEW- NEW- NEW- NEW- NEW- NEW- NEW- NEW- NEW*/
void Object3D::CalcAnimation(ObjectBone* &pBone)
{
	pBone->CalcAnimation(_cKey);
	if (!pBone->_Bones.empty())
 		for(std::list<ObjectBone*>::iterator i = pBone->_Bones.begin(); i != pBone->_Bones.end(); i++)
        	CalcAnimation(*i);
}
/***END*******************************************/

void Object3D::CalcBindSpace(ObjectBone* &pBone)
{
	pBone->CalcBindSpace();
	if (!pBone->_Bones.empty())
 		for(std::list<ObjectBone*>::iterator i = pBone->_Bones.begin(); i != pBone->_Bones.end(); i++)
        	CalcBindSpace(*i);
}

void Object3D::SkinMesh(ObjectBone* &pBone)
{
   if (!pBone->_Bones.empty())
 		for(std::list<ObjectBone*>::iterator i = pBone->_Bones.begin(); i != pBone->_Bones.end(); i++)
        	SkinMesh(*i);/**/

	Frm::Vertex* MeshVertices = _Mesh->_Vertices;
   uint32 nIndices = pBone->_Bone->_nVertices;
   uint16* VertexIndices = pBone->_Bone->_Vertices;
   float* Weights = pBone->_Bone->_Weights;

   for(int i = 0; i < nIndices; i++)
      _SkinnedVertices[VertexIndices[i]] += (pBone->_FinalMatrix * MeshVertices[VertexIndices[i]]) * Weights[i];

}/**/

void Object3D::ComputeBoundingBoxSphere(void)
{
	_Low = _SkinnedVertices[0];
   _High = _Low;
   _Center = _Low;
   _Radius = 0.0f;
	for(int i = 0; i < _Mesh->_nVertices; i++)
   	{
      if (_Low[0]> _SkinnedVertices[i][0])
      	_Low[0] = _SkinnedVertices[i][0];
      if (_Low[1]> _SkinnedVertices[i][1])
      	_Low[1] = _SkinnedVertices[i][1];
      if (_Low[2]> _SkinnedVertices[i][2])
      	_Low[2] = _SkinnedVertices[i][2];
      if (_High[0]< _SkinnedVertices[i][0])
      	_High[0] = _SkinnedVertices[i][0];
      if (_High[1]< _SkinnedVertices[i][1])
      	_High[1] = _SkinnedVertices[i][1];
      if (_High[2]< _SkinnedVertices[i][2])
      	_High[2] = _SkinnedVertices[i][2];
      }
   	_Center[0] = _Low[0] + (_High[0] - _Low[0])*0.5f;
      _Center[1] = _Low[1] + (_High[1] - _Low[1])*0.5f;
      _Center[2] = _Low[2] + (_High[2] - _Low[2])*0.5f;

      MYTRACE("AABB Low:", _Low[0], "x", _Low[1], "x", _Low[2]);
      MYTRACE("AABB High:", _High[0], "x", _High[1], "x", _High[2]);
      MYTRACE("AABB Center:", _Center[0], "x", _Center[1], "x", _Center[2]);
}
